import arrow
import ssl
import socket
from smtplib import SMTPException
from mongoengine import Q

from wass.settings import TIME_ZONE
from models.meta import UpdateOne, DeleteOne
from models.warn import WebWarnings, WebInboxMails
from models.target import Targets, TargetGroups
from apps.warn_center.serializer import (
    InboxMailSerializer,
    ReceiversSerializer,
    WarnEmailSerializer,
    WarnEmailTestSerializer,
    WarnNetMapSerializer,
)
from utilities.meta_view import (
    APIViewList,
    APIViewUpdate,
    APIViewDestroy,
    Response,
    Success,
    APIViewCreate,
    APIViewRetrieve,
)
from utilities.utils import filter_cha
from utilities.email.wass_email import WassEmailSender
from utilities.enums import (
    EmailTemplate,
    TaskType,
    LogModuleName,
    LogTypeName,
    AuditLogAction,
    UTC_ISO_FORMAT,
)
from utilities.log import audit_log


class ListWarnings(APIViewList):
    allow_keys = ('created_at', 'content', 'task_type', 'task_session_id', 'target_id')
    check_user = True

    def list(self):
        page, limit = self.get_paginate()

        target_name = self.request.GET.get('target_name')
        target_group_name = self.request.GET.get('target_group_name')
        task_type = self.request.GET.get('task_type')
        time_low = self.request.GET.get('time_low')
        time_high = self.request.GET.get('time_high')

        query = Q(**self.user_query)
        ex_query = Q(**self.user_query)
        if task_type:
            query = query & Q(task_type=task_type)
        if time_low:
            query = query & Q(created_at__gt=arrow.get(time_low).datetime)
        if time_high:
            query = query & Q(created_at__lte=arrow.get(time_high).datetime)

        if target_name:
            target_name = filter_cha(target_name)
            ex_query = ex_query & (
                Q(target_name__contains=target_name)
                | (Q(target_name='') & Q(target_title__contains=target_name))
            )
        allow_target_group_keys = ('target_group_name', 'target_group_id')
        target_group_map = {}
        target_group_ids = set()
        if target_group_name:
            task_group_query = Q(
                target_group_name__contains=filter_cha(target_group_name),
                **self.user_query,
            )
            for item in TargetGroups.objects.only(*allow_target_group_keys).filter(
                task_group_query
            ):
                target_group_id = item.target_group_id
                target_group_ids.add(target_group_id)
                target_group_map[target_group_id] = {
                    'target_group_name': item.target_group_name,
                }
        if target_group_ids:
            ex_query = ex_query & Q(target_group_id__in=target_group_ids)
        allow_target_keys = (
            'target_id',
            'target_group_id',
            'target_url',
            'target_name',
            'target_title',
        )
        target_ids = set()
        target_map = {}
        for target in Targets.objects.only(*allow_target_keys).filter(ex_query):
            target_id = target.target_id
            target_ids.add(target_id)
            target_map[target_id] = {
                'target_id': target_id,
                'target_group_id': target.target_group_id,
                'target_url': target.target_url,
                'target_name': target.target_name,
                'target_title': target.target_title,
            }
        if not target_group_name:
            task_group_query = Q(
                target_group_id__in=[
                    target['target_group_id'] for target in target_map.values()
                ]
            )
            for item in TargetGroups.objects.only(*allow_target_group_keys).filter(
                task_group_query
            ):
                target_group_map[item.target_group_id] = {
                    'target_group_name': item.target_group_name,
                }

        if target_ids:
            query = query & Q(target_id__in=target_ids)
        all_warnings = (
            WebWarnings.objects.filter(query).order_by('-created_at').paginate(limit)
        )
        warnings = all_warnings.page(page)

        warnings_result = []
        for warning in warnings:
            target_id = warning.target_id
            target_group_id = warning.target_group_id
            warnings_result.append(
                {
                    'warning_id': warning.warning_id,
                    'target_id': target_id,
                    'target_name': target_map[target_id]['target_name']
                    or target_map[target_id]['target_title'],
                    'target_url': target_map[target_id]['target_url'],
                    'target_group_id': target_group_id,
                    'task_session_id': warning.task_session_id,
                    'target_group_name': target_group_map[target_group_id][
                        'target_group_name'
                    ],
                    'user_id': warning.user_id,
                    'task_type': warning.task_type,
                    'content': warning.content,
                    'created_at': arrow.get(warning.created_at).isoformat(),
                    'updated_at': arrow.get(warning.updated_at).isoformat(),
                }
            )

        return Response(
            {'data': warnings_result, 'page': page, 'total': all_warnings.count}
        )


class InboxMailBatchHandle(APIViewUpdate, APIViewDestroy):
    serializer_class = InboxMailSerializer
    check_user = True

    def update(self):
        inbox_mail_id_list = self.request.data.get('inbox_mail_ids', [])
        status = int(self.request.data.get('status'))

        serializer = self.get_serializer(data=self.request.data)
        serializer.is_valid(raise_exception=True)

        now_datetime = arrow.utcnow().datetime
        update_list = []
        for _id in inbox_mail_id_list:
            update_list.append(
                UpdateOne(
                    {
                        getattr(WebInboxMails, '_meta').get('primary_key'): _id,
                        **self.user_query,
                    },
                    {'$set': {'updated_at': now_datetime, 'status': status}},
                )
            )
        serializer.bulk_write(update_list)

        log_data = {
            "module": LogModuleName.warn_center.value,
            "action": AuditLogAction.up.value,
            "type": LogTypeName.warn_center.value,
            "desc": {"detail": f"标记站内信为{'已读' if status == 1 else '未读'}"},
        }
        audit_log(self.request, **log_data)

        return Success()

    def destroy(self):
        inbox_mail_id_list = self.request.data.get('inbox_mail_ids', [])

        serializer = self.get_serializer(data=self.request.data)

        delete_list = []
        for _id in inbox_mail_id_list:
            delete_list.append(
                DeleteOne(
                    {
                        getattr(WebInboxMails, '_meta').get('primary_key'): _id,
                        **self.user_query,
                    }
                )
            )
        serializer.bulk_write(delete_list)

        log_data = {
            "module": LogModuleName.warn_center.value,
            "action": AuditLogAction.del_.value,
            "type": LogTypeName.warn_center.value,
            "desc": {"detail": "批量删除站内信"},
        }
        audit_log(self.request, **log_data)

        return Success()


class InboxMailBatchAllHandle(APIViewUpdate, APIViewDestroy):
    serializer_class = InboxMailSerializer
    queryset = InboxMailSerializer.Meta.model.objects
    check_user = True

    def update(self):
        status = int(self.request.data.get('status'))
        serializer = self.get_serializer(data=self.request.data)
        serializer.is_valid(raise_exception=True)

        WebInboxMails.objects.filter(**self.user_query).update(status=status)

        log_data = {
            "module": LogModuleName.warn_center.value,
            "action": AuditLogAction.up.value,
            "type": LogTypeName.warn_center.value,
            "desc": {"detail": f"标记所有站内信为{'已读' if status == 1 else '未读'}"},
        }
        audit_log(self.request, **log_data)

        return Success()

    def destroy(self):
        WebInboxMails.objects.filter(**self.user_query).delete()

        log_data = {
            "module": LogModuleName.warn_center.value,
            "action": AuditLogAction.del_.value,
            "type": LogTypeName.warn_center.value,
            "desc": {"detail": "批量删除所有站内信"},
        }
        audit_log(self.request, **log_data)

        return Success()


class ListInboxMails(APIViewList):
    serializer_class = InboxMailSerializer
    check_user = True

    def list(self):
        page, limit = self.get_paginate()
        status = self.request.GET.get('status')
        query = {**self.user_query}
        if status:
            query.update({'status': int(status)})

        all_mails = WebInboxMails.objects.filter(**query).paginate(limit)
        mails = all_mails.get_page(page)
        serializer = self.get_serializer(mails, many=True)

        return Response(
            {'data': serializer.data, 'page': page, 'total': all_mails.count}
        )


class ListReceivers(APIViewList):
    serializer_class = ReceiversSerializer

    def list(self):
        page, limit = self.get_paginate()
        is_get_all = int(self.request.GET.get('all', 0))

        q = Q()
        if self.user_query:
            q = Q(**self.user_query) | Q(receiver_type=0)
        all_receivers = self.serializer_class.Meta.model.objects.filter(q).paginate(
            limit
        )

        if not is_get_all:
            receivers = all_receivers.get_page(page)
        else:
            receivers = all_receivers.object_list
        serializer = self.get_serializer(receivers, many=True)

        return Response(
            {
                'data': sorted(serializer.data, key=lambda x: x['receiver_type']),
                'page': page,
                'total': all_receivers.count,
            }
        )


class ReceiversHandle(APIViewRetrieve, APIViewCreate, APIViewUpdate, APIViewDestroy):
    serializer_class = ReceiversSerializer
    queryset = ReceiversSerializer.Meta.model.objects
    lookup_field = 'receiver_id'

    def create(self):
        resp = super(ReceiversHandle, self).create()

        log_data = {
            "module": LogModuleName.warn_center.value,
            "action": AuditLogAction.create.value,
            "type": LogTypeName.warn_center.value,
            "desc": {
                "detail": f"创建告警联系人 '{self.request.data.get('contact_name', 'unknown')}'"
            },
        }
        audit_log(self.request, **log_data)

        return resp

    def update(self):
        res = super(ReceiversHandle, self).update()

        log_data = {
            "module": LogModuleName.warn_center.value,
            "action": AuditLogAction.del_.value,
            "type": LogTypeName.warn_center.value,
            "desc": {
                "detail": f"编辑告警联系人 '{self.request.data.get('contact_name', 'unknown')}'"
            },
        }
        audit_log(self.request, **log_data)

        return res

    def destroy(self):
        receiver_id_list = self.request.data.get('receiver_ids', [])

        obj_receiver_list = self.serializer_class.Meta.model.objects.find(
            {self.lookup_field: {'$in': receiver_id_list}, 'receiver_type': {'$ne': 0}}
        )

        receiver_name_list = [item.contact_name for item in obj_receiver_list]
        obj_receiver_list.delete()

        log_data = {
            "module": LogModuleName.warn_center.value,
            "action": AuditLogAction.del_.value,
            "type": LogTypeName.warn_center.value,
            "desc": {"detail": f"删除告警联系人 '{', '.join(receiver_name_list)}'"},
        }
        audit_log(self.request, **log_data)

        return Success()


class WarnEmailHandle(APIViewRetrieve, APIViewUpdate):
    serializer_class = WarnEmailSerializer

    def retrieve(self):
        global_settings = self.serializer_class.Meta.model.objects.find_one()
        serializer = self.get_serializer(
            getattr(global_settings, 'email_settings', None)
        )

        return Response({'data': serializer.data})

    def update(self):
        global_settings = self.serializer_class.Meta.model.objects.find_one()
        serializer = self.get_serializer(global_settings, data=self.request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        log_data = {
            "module": LogModuleName.warn_center.value,
            "action": AuditLogAction.up.value,
            "type": LogTypeName.warn_center.value,
            "desc": {"detail": "更新告警邮件配置"},
        }
        audit_log(self.request, **log_data)

        return Success()


class WarnEmailTest(APIViewCreate):
    serializer_class = WarnEmailTestSerializer

    def create(self):
        serializer = self.get_serializer(data=self.request.data)
        serializer.is_valid(raise_exception=True)

        args = {
            'to': serializer.validated_data.get('receiver_ids', []),
            'title': serializer.validated_data.get('title', ''),
            'body': {'%content%': serializer.validated_data.get('content', '')},
        }
        log_data = {
            "module": LogModuleName.warn_center.value,
            "action": AuditLogAction.create.value,
            "type": LogTypeName.warn_center.value,
            "desc": {"detail": "发送测试邮件成功"},
        }
        is_success = True
        message = ""
        try:
            WassEmailSender().send(EmailTemplate.test.value, args)
        except ssl.SSLError:
            message = "发送测试邮件失败, 配置的邮件服务器可能不支持SSL加密"
            log_data["desc"]["detail"] = message
            is_success = False
        except Exception:
            message = "发送测试邮件失败, 请检查邮箱配置"
            log_data["desc"]["detail"] = "发送测试邮件失败"
            is_success = False
        audit_log(self.request, **log_data)
        if not is_success:
            return Response(
                data={"status": "fail", "errcode": -1, "message": message}, status=400
            )
        return Success()


class WarnSummaryRecentWeek(APIViewRetrieve):
    check_user = True

    def retrieve(self):
        arrow_datetime_now = arrow.now(tz=TIME_ZONE)
        arrow_datetime_low = arrow_datetime_now.shift(weeks=-1, days=1)
        datetime_low = arrow_datetime_low.datetime

        date_dict = {}
        for i in range(7):
            iso_date = (
                arrow_datetime_now.replace(hour=0, minute=0, second=0, microsecond=0)
                .shift(days=-i)
                .isoformat()
            )
            date = arrow_datetime_now.shift(days=-i).date().isoformat()
            date_dict[date] = {iso_date: 0}

        query = {'created_at': {'$gte': datetime_low}, **self.user_query}
        pipeline = [
            {'$match': query},
            {
                '$project': {
                    'created_at': {
                        '$dateToString': {
                            'format': UTC_ISO_FORMAT,
                            'date': "$created_at",
                            'timezone': 'UTC',
                        }
                    },
                    'warning_id': 1,
                }
            },
            {'$group': {'_id': '$created_at', 'warnings': {'$push': '$warning_id'}}},
            {'$project': {'count': {'$size': '$warnings'}}},
            {'$sort': {'_id': -1}},
        ]

        total = 0

        arrow_date_count_map = dict()
        for item in WebWarnings.objects.aggregate(pipeline):
            count = item.get('count', 0)
            arrow_date = arrow.get(item['_id'])
            key = arrow_date.date().isoformat()
            if key in arrow_date_count_map:
                arrow_date_count_map[key] += 1
            else:
                arrow_date_count_map[key] = 1
            total += count

        for k, v in arrow_date_count_map.items():
            date_dict[k] = {k: v}

        ctx = {
            'trend': list(date_dict.values()),
            'total': total,
            'warnings_total': WebWarnings.objects.find(self.user_query).count(),
        }

        return Response({'data': ctx})


class WarnSummaryRecentDay(APIViewRetrieve):
    check_user = True

    def retrieve(self):
        datetime_low = (
            arrow.now(tz=TIME_ZONE)
            .replace(hour=0, minute=0, second=0, microsecond=0)
            .datetime
        )

        query = {'created_at': {'$gte': datetime_low}, **self.user_query}
        pipeline = [
            {'$match': query},
            {
                '$lookup': {
                    'from': getattr(Targets, '_meta')['collection'],
                    'localField': 'target_id',
                    'foreignField': 'target_id',
                    'as': 'target',
                }
            },
            {'$unwind': '$target'},
        ]

        total = 0
        group_target_id = {}
        group_task_type = {item.value: 0 for item in TaskType}

        for item in WebWarnings.objects.aggregate(pipeline):
            target_id = item.get('target_id')
            task_type = item.get('task_type')
            task_session_id = item.get('task_session_id')
            target = item.get('target', {})
            target_url = target.get('target_url')
            target_name = target.get('target_name') or target.get('target_title', '')

            group_target_id.setdefault(
                target_id,
                {
                    'target_id': target_id,
                    'task_session_id': task_session_id,
                    'target_name': target_name,
                    'target_url': target_url,
                    'count': 0,
                },
            )['count'] += 1
            group_task_type[task_type] = group_task_type.get(task_type, 0) + 1

            total += 1

        top_list = sorted(
            group_target_id.values(), key=lambda x: x['count'], reverse=True
        )[:5]

        ctx = {'top': top_list, 'proportion': group_task_type, 'total': total}

        return Response({'data': ctx})


class WarnNepMapHandle(APIViewRetrieve, APIViewUpdate):
    serializer_class = WarnNetMapSerializer

    def retrieve(self):
        global_settings = self.serializer_class.Meta.model.objects.find_one()
        serializer = self.get_serializer(global_settings)

        return Response({'data': serializer.data})

    def update(self):
        global_settings = self.serializer_class.Meta.model.objects.find_one()
        serializer = self.get_serializer(global_settings, data=self.request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        log_data = {
            "module": LogModuleName.warn_center.value,
            "action": AuditLogAction.up.value,
            "type": LogTypeName.warn_center.value,
            "desc": {"detail": "更新外网映射配置"},
        }
        audit_log(self.request, **log_data)

        return Success()
